package com.hyper_yang.javase.supplement.lambda;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.function.*;

/*
使用 lambda，接口必须是一个函数式接口
    SAM: Single Abstract Method 一个接口中只有一个抽象方法

如何识别一个函数式接口: @FunctionalInterface

java内置提供的函数式接口: consumer, supplier, predicate, function
    消费型: 有形参没有返回值
    供给型: 没有形参有返回值
    断言型: 有形参有 boolean返回值
    功能型: 有形参有返回值
 ()->{}
    (): 代表当前接口中唯一的一个抽象方法
    ->: lambda表达式的标识，指向实现过程
    {}: 当前接口唯一的一个抽象方法的实现过程
 */

interface A {
    String concat(String s1, String s2);
}

/**
 * @param <T>  返回值类型
 * @param <V1> 方法的第一个形参
 * @param <V2> 方法的第二个形参
 */
interface B<T, V1, V2> {
    T create(V1 v1, V2 v2);
}

public class lambdaTest {
    @Test
    public void testLambda() {
        String[] arr = {"hello", "world", "常勇康", "康师傅啊"};
//        Arrays.sort(arr, new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o1.length() - o2.length();
//            }
//        });
        Arrays.sort(arr, (o1, o2) -> o2.length() - o1.length());

        System.out.println(Arrays.toString(arr));
    }

    @DisplayName("consumer: ")
    @Test // 有形参，没有返回值 accept()
    public void testConsumer() {
        ArrayList<Integer> arrayList = new ArrayList<>();
        Collections.addAll(arrayList, 4, 2, 8, 0, 6);
//        arrayList.forEach(new Consumer<Integer>() {
//                              @Override
//                              public void accept(Integer integer) {
//                                  System.out.print(integer + '\t');
//                              }
//                          }
//        );
        arrayList.forEach(i -> System.out.print(i + "\t"));
        System.out.println("\n" + "==========================================");
        HashSet<Integer> hashSet = new HashSet<>();
        Collections.addAll(hashSet, 3, 1, 6, 9, 8);
//        hashSet.forEach(new Consumer<Integer>() {
//            @Override
//            public void accept(Integer integer) {
//                System.out.print(integer + '\t');
//            }
//        });
        hashSet.forEach(i -> System.out.print(i + "\t"));
//        hashSet.forEach(System.out::println); // 方法引用
        System.out.println("\n" + "==========================================");
        HashMap<Integer, String> students = new HashMap<>();
        students.put(1, "张三");
        students.put(2, "李四");
        students.put(3, "王五");
        students.put(4, "赵六");
//        students.forEach(new BiConsumer<Integer, String>() {
//            @Override
//            public void accept(Integer integer, String string) {
//                System.out.println(integer + '\t' + string);
//            }
//        });
        students.forEach((key, value) -> System.out.println(key + "\t" + value));
    }

    @DisplayName("supplier: ")
    @Test // 没有形参，有返回值 get()
    public void testSupplier() {
//        Supplier<Double> supplier = new Supplier<Double>() {
//            @Override
//            public Double get() {
//                return Math.random();
//            }
//        };
//        Supplier<Double> supplier = () -> Math.random();
        Supplier<Double> supplier = Math::random;
        System.out.print(supplier.get());
        System.out.println("\n" + "==========================================");
//        Supplier<Double[]> supplier1 = new Supplier<Double[]>() {
//            @Override
//            public Double[] get() {
//                Double[] ds = new Double[10];
//                for (int i = 0; i < ds.length; i++) {
//                    ds[i] = Math.random();
//                }
//                return ds;
//            }
//        };
        Supplier<Double[]> supplier1 = () -> {
            Double[] ds = new Double[10];
            for (int i = 0; i < ds.length; i++) {
                ds[i] = Math.random();
            }
            return ds;
        };
        Double[] ds = supplier1.get();
        System.out.println(Arrays.toString(ds));
    }

    @DisplayName("predicate: ")
    @Test // 有形参，有 boolean返回值 test()
    public void testPredicate() {
        ArrayList<String> brand = new ArrayList<>();
        Collections.addAll(brand, "Sk-II", "Prada", "YSL", "Channel", "Gucci", "LV");
        // 遍历，判断是否带有 a，有则删除
//        brand.removeIf(new Predicate<String>() {
//            @Override
//            public boolean test(String string) {
//                return string.contains("a");
//            }
//        });
        brand.removeIf(s -> s.contains("a"));
        System.out.println(brand);
    }

    @DisplayName("function: ")
    @Test // 有形参，有返回值
    public void testFunction() {
        Map<String, String> maps = new HashMap<>();
        maps.put("s1", "第一赛季");
        maps.put("s2", "第二赛季");
        maps.put("s3", "第三赛季");
        maps.put("s4", "第四赛季");
        maps.put("s5", "第五赛季");
//        maps.replaceAll(new BiFunction<String, String, String>() {
//            /**
//             * @param s the first function argument
//             * @param s2 the second function argument
//             * @return
//             */
//            @Override
//            public String apply(String s, String s2) {
//                return s2.replaceAll("第", "S赛");
//            }
//        });
        maps.replaceAll((k, v) -> v.replaceAll("第", "S赛"));

//        maps.replaceAll((k, v) -> k.toUpperCase()); 操作会影响 value
//    deepseek:
//    replaceAll 只能修改 value，无法直接修改 key。
//    Lambda表达式 (k, v) -> k.toUpperCase() 的返回值是新 value，与 key无关。
//    修改 key需要重建 Map（因为 Map的键是 final的，无法原地修改）

        maps.forEach((k, v) -> System.out.println((k + "\t" + v)));
    }

    @DisplayName("方法引用: ")
    @Test
    public void testReference() {
/*  【是用来简写 lambda的】
    方法引用: 前提 -- lambda表达式
            在书写 lambda表达式的场景下，满足特定的要求，可以简写成方法引用
            类名::静态方法     --- 重写后的方法和实现过程调用的方法是匹配的
            对象名::实例方法    --- 重写后的方法和实现过程调用的方法是匹配的
            类名::实例方法     --- 本质还是对象调方法
                    有一个形参，且形参作为方法实现过程的调用者
                    有多个形参，第一个形参作为实现过程的方法调用者，后续的形参方法的实参
 */
        // todo: 类名::静态方法
//        Supplier<Double> supplier = () -> Math.random();
        Supplier<Double> supplier = Math::random;
        System.out.println(supplier.get());

        // todo: 对象名::实例方法
        Person person = new Person("张三", 20);
//        person.sayHi("今日天气真好");
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                person.sayHi(s);
//            }
//        };
//        Consumer<String> consumer = (s) -> person.sayHi(s);
        Consumer<String> consumer = person::sayHi;
        consumer.accept("have a good day!!!");

        // todo: 类名::实例方法
//        Function<String,Integer> function = new Function<String, Integer>() {
//            @Override
//            public Integer apply(String s) {
//                return s.length();
//            }
//        };
//        Function<String, Integer> function = s -> s.length();
        Function<String, Integer> function = String::length;
        System.out.println(function.apply("compute the s length"));

//        A a = new A() {
//            @Override
//            public String concat(String s1, String s2) {
//                return s1.concat(s2);
//            }
//        };
//        A a = (s1, s2) -> s1.concat(s2);
        A a = String::concat;
        System.out.println(a.concat("莫道桑榆晚", "为霞尚满天"));
    }

    @DisplayName("方法引用二: ")
    @Test
    public void testReference2() {
/*
    方法引用补充: 类名::new 当 lambda表达式是一个 new表达式，并且 lambda表达式的形参正好依次按顺序作为所调用构造器的实参。
                    重写的方法和方法的实现过程匹配，且实现过程是 new对象
               数组类型名[]::new 当 lambda表达式是一个创建数组对象的 new表达式，lamda表达式的形参正好是创建数组的长度。
 */
        // todo: 类名::new
        Person person = new Person();
//        Supplier<Person> supplier = new Supplier<Person>() {
//            @Override
//            public Person get() {
//                return new Person();
//            }
//        };
//        Supplier<Person> supplier = ()->new Person();
        Supplier<Person> supplier = Person::new;
        System.out.println(supplier.get());

//        Person person = new Person("张三", 23);
//        B<Person, String, Integer> b = new B<Person, String, Integer>() {
//            @Override
//            public Person create(String name, Integer age) {
//                return new Person(name, age);
//            }
//        };
//        B<Person, String, Integer> b = (name, age) -> new Person(name, age);
        B<Person, String, Integer> b = Person::new;
        System.out.println(b.create("李四", 24));

        // todo: 数组类型名::new
//        Function<Integer, Integer[]> function = new Function<Integer, Integer[]>() {
//            @Override
//            public Integer[] apply(Integer i) {
//                return new Integer[i];
//            }
//        };
//        Function<Integer, Integer[]> function = i -> new Integer[i];
        Function<Integer, Integer[]> function = Integer[]::new;
        System.out.println(function.apply(5).length);
    }
}

class Person {
    String name;
    Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public void sayHi(String message) {
        System.out.println("sayHi: " + message);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
